package com.stu.jvm;

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.util.ArrayList;
import java.util.List;

/**
 * JVM内存模型详细演示
 * 涵盖：
 * - JVM内存区域划分
 * - 堆内存结构（年轻代、老年代）
 * - 方法区/元空间
 * - 程序计数器、虚拟机栈、本地方法栈
 * - 直接内存
 * - 内存溢出演示
 * 
 * @author 学习者
 * @version 1.0
 */
public class JVMMemoryModel {
    
    // 静态变量存储在方法区
    private static final String STATIC_CONSTANT = "存储在方法区的常量";
    private static List<String> staticList = new ArrayList<>();
    
    // 实例变量存储在堆中
    private String instanceVariable = "存储在堆中的实例变量";
    private List<Object> instanceList = new ArrayList<>();
    
    public static void main(String[] args) {
        System.out.println("=== JVM内存模型详细演示 ===");
        
        // 1. JVM内存区域概述
        demonstrateMemoryAreas();
        
        // 2. 堆内存演示
        demonstrateHeapMemory();
        
        // 3. 方法区演示
        demonstrateMethodArea();
        
        // 4. 虚拟机栈演示
        demonstrateVMStack();
        
        // 5. 程序计数器演示
        demonstrateProgramCounter();
        
        // 6. 本地方法栈演示
        demonstrateNativeMethodStack();
        
        // 7. 直接内存演示
        demonstrateDirectMemory();
        
        // 8. 内存使用情况查看
        showMemoryUsage();
        
        // 9. 内存溢出演示（注释掉避免程序崩溃）
        // demonstrateOutOfMemoryError();
    }
    
    /**
     * JVM内存区域概述
     */
    private static void demonstrateMemoryAreas() {
        System.out.println("\n=== JVM内存区域划分 ===");
        
        System.out.println("\nJVM内存主要分为以下几个区域：");
        
        System.out.println("\n1. 程序计数器（Program Counter Register）");
        System.out.println("   - 线程私有");
        System.out.println("   - 存储当前线程执行的字节码指令地址");
        System.out.println("   - 唯一不会发生OutOfMemoryError的区域");
        System.out.println("   - 如果执行Native方法，计数器值为空（Undefined）");
        
        System.out.println("\n2. 虚拟机栈（Java Virtual Machine Stack）");
        System.out.println("   - 线程私有");
        System.out.println("   - 存储局部变量、操作数栈、动态链接、方法出口");
        System.out.println("   - 每个方法调用创建一个栈帧（Stack Frame）");
        System.out.println("   - 可能抛出StackOverflowError和OutOfMemoryError");
        
        System.out.println("\n3. 本地方法栈（Native Method Stack）");
        System.out.println("   - 线程私有");
        System.out.println("   - 为Native方法服务");
        System.out.println("   - HotSpot虚拟机将其与虚拟机栈合并");
        
        System.out.println("\n4. 堆（Heap）");
        System.out.println("   - 线程共享");
        System.out.println("   - 存储对象实例和数组");
        System.out.println("   - 垃圾收集的主要区域");
        System.out.println("   - 分为年轻代（Young Generation）和老年代（Old Generation）");
        
        System.out.println("\n5. 方法区（Method Area）/ 元空间（Metaspace）");
        System.out.println("   - 线程共享");
        System.out.println("   - 存储类信息、常量、静态变量、即时编译器编译后的代码");
        System.out.println("   - JDK8之前叫永久代，JDK8之后改为元空间");
        
        System.out.println("\n6. 直接内存（Direct Memory）");
        System.out.println("   - 不是JVM运行时数据区的一部分");
        System.out.println("   - NIO使用Native函数库直接分配堆外内存");
        System.out.println("   - 不受Java堆大小限制，但受本机总内存限制");
        
        System.out.println("\n内存区域线程安全性：");
        System.out.println("线程私有：程序计数器、虚拟机栈、本地方法栈");
        System.out.println("线程共享：堆、方法区");
    }
    
    /**
     * 堆内存演示
     */
    private static void demonstrateHeapMemory() {
        System.out.println("\n=== 堆内存结构演示 ===");
        
        System.out.println("\n堆内存分代结构：");
        System.out.println("1. 年轻代（Young Generation）");
        System.out.println("   - Eden区：新对象分配的地方");
        System.out.println("   - Survivor区：分为S0和S1，存放经过一次GC后存活的对象");
        System.out.println("   - 默认比例 Eden:S0:S1 = 8:1:1");
        
        System.out.println("\n2. 老年代（Old Generation）");
        System.out.println("   - 存放长期存活的对象");
        System.out.println("   - 经过多次GC仍然存活的对象会被移到老年代");
        System.out.println("   - 大对象可能直接分配到老年代");
        
        // 演示对象在堆中的分配
        System.out.println("\n对象分配演示：");
        
        // 创建小对象（通常分配在Eden区）
        String smallObject = new String("小对象");
        System.out.println("创建小对象：" + smallObject + " -> 分配在Eden区");
        
        // 创建数组对象
        int[] array = new int[1000];
        System.out.println("创建数组对象：长度1000的int数组 -> 分配在堆中");
        
        // 创建对象引用链
        JVMMemoryModel instance = new JVMMemoryModel();
        instance.instanceList.add("对象1");
        instance.instanceList.add("对象2");
        System.out.println("创建对象引用链 -> 对象和引用都在堆中");
        
        System.out.println("\n对象生命周期：");
        System.out.println("1. 新对象在Eden区分配");
        System.out.println("2. Eden区满时触发Minor GC");
        System.out.println("3. 存活对象移到Survivor区，年龄+1");
        System.out.println("4. Survivor区对象在S0和S1之间复制");
        System.out.println("5. 年龄达到阈值（默认15）或Survivor区满时，移到老年代");
        System.out.println("6. 老年代满时触发Major GC或Full GC");
        
        System.out.println("\n大对象处理：");
        System.out.println("- 大对象直接分配到老年代，避免在年轻代复制");
        System.out.println("- 大对象阈值由-XX:PretenureSizeThreshold参数控制");
        System.out.println("- 长字符串和大数组是典型的大对象");
        
        // 演示字符串常量池
        System.out.println("\n字符串常量池演示：");
        String str1 = "Hello"; // 字面量，存储在字符串常量池
        String str2 = "Hello"; // 复用常量池中的对象
        String str3 = new String("Hello"); // 在堆中创建新对象
        
        System.out.println("str1 == str2: " + (str1 == str2)); // true
        System.out.println("str1 == str3: " + (str1 == str3)); // false
        System.out.println("str1.equals(str3): " + str1.equals(str3)); // true
        
        System.out.println("\n说明：");
        System.out.println("- 字符串字面量存储在字符串常量池（JDK7后在堆中）");
        System.out.println("- new String()会在堆中创建新对象");
        System.out.println("- intern()方法可以将字符串加入常量池");
    }
    
    /**
     * 方法区演示
     */
    private static void demonstrateMethodArea() {
        System.out.println("\n=== 方法区/元空间演示 ===");
        
        System.out.println("\n方法区存储内容：");
        System.out.println("1. 类信息（Class对象）");
        System.out.println("2. 常量池（Constant Pool）");
        System.out.println("3. 静态变量（Static Variables）");
        System.out.println("4. 即时编译器编译后的代码（JIT Code）");
        
        // 演示类信息存储
        System.out.println("\n类信息演示：");
        Class<?> clazz = JVMMemoryModel.class;
        System.out.println("类名：" + clazz.getName());
        System.out.println("父类：" + clazz.getSuperclass().getName());
        System.out.println("方法数量：" + clazz.getDeclaredMethods().length);
        System.out.println("字段数量：" + clazz.getDeclaredFields().length);
        System.out.println("这些类的元信息存储在方法区中");
        
        // 演示静态变量
        System.out.println("\n静态变量演示：");
        staticList.add("静态数据1");
        staticList.add("静态数据2");
        System.out.println("静态变量STATIC_CONSTANT: " + STATIC_CONSTANT);
        System.out.println("静态集合staticList: " + staticList);
        System.out.println("静态变量的引用存储在方法区，但对象实例仍在堆中");
        
        // 演示常量池
        System.out.println("\n运行时常量池演示：");
        final String FINAL_CONSTANT = "编译时常量";
        String runtimeConstant = "运行时" + "常量";
        System.out.println("编译时常量：" + FINAL_CONSTANT);
        System.out.println("运行时常量：" + runtimeConstant);
        System.out.println("常量池存储在方法区中，包含字面量和符号引用");
        
        System.out.println("\nJDK版本差异：");
        System.out.println("JDK7及之前：");
        System.out.println("  - 方法区实现为永久代（PermGen）");
        System.out.println("  - 字符串常量池在永久代中");
        System.out.println("  - 容易发生OutOfMemoryError: PermGen space");
        
        System.out.println("\nJDK8及之后：");
        System.out.println("  - 方法区实现为元空间（Metaspace）");
        System.out.println("  - 使用本地内存，不再受堆大小限制");
        System.out.println("  - 字符串常量池移到堆中");
        System.out.println("  - 静态变量移到堆中的Class对象中");
        
        System.out.println("\n元空间优势：");
        System.out.println("1. 使用本地内存，减少OOM风险");
        System.out.println("2. 自动扩展，默认无大小限制");
        System.out.println("3. 提高GC效率，减少Full GC频率");
        System.out.println("4. 类卸载更加及时");
    }
    
    /**
     * 虚拟机栈演示
     */
    private static void demonstrateVMStack() {
        System.out.println("\n=== 虚拟机栈演示 ===");
        
        System.out.println("\n虚拟机栈结构：");
        System.out.println("每个线程都有独立的虚拟机栈");
        System.out.println("栈中存储栈帧（Stack Frame），每个方法调用对应一个栈帧");
        
        System.out.println("\n栈帧包含：");
        System.out.println("1. 局部变量表（Local Variables）");
        System.out.println("2. 操作数栈（Operand Stack）");
        System.out.println("3. 动态链接（Dynamic Linking）");
        System.out.println("4. 方法返回地址（Return Address）");
        
        // 演示方法调用和栈帧
        System.out.println("\n方法调用栈演示：");
        System.out.println("当前方法：demonstrateVMStack() -> 创建栈帧1");
        
        // 调用其他方法演示栈帧创建
        stackMethod1();
        
        System.out.println("\n局部变量表演示：");
        int localInt = 100;           // 基本类型存储在局部变量表
        String localString = "局部变量"; // 引用类型的引用存储在局部变量表
        Object localObject = new Object(); // 对象在堆中，引用在局部变量表
        
        System.out.println("localInt: " + localInt + " -> 值存储在局部变量表");
        System.out.println("localString: " + localString + " -> 引用存储在局部变量表，对象在堆中");
        System.out.println("localObject: " + localObject + " -> 引用存储在局部变量表，对象在堆中");
        
        System.out.println("\n栈内存特点：");
        System.out.println("1. 线程私有，每个线程独立");
        System.out.println("2. 生命周期与线程相同");
        System.out.println("3. 方法执行完毕，栈帧自动销毁");
        System.out.println("4. 局部变量随栈帧销毁而销毁");
        System.out.println("5. 不需要垃圾回收");
        
        System.out.println("\n可能的异常：");
        System.out.println("1. StackOverflowError：栈深度超过限制（如无限递归）");
        System.out.println("2. OutOfMemoryError：栈内存不足（多线程场景）");
    }
    
    /**
     * 栈方法调用演示1
     */
    private static void stackMethod1() {
        System.out.println("  调用stackMethod1() -> 创建栈帧2");
        stackMethod2();
        System.out.println("  stackMethod1()执行完毕 -> 销毁栈帧2");
    }
    
    /**
     * 栈方法调用演示2
     */
    private static void stackMethod2() {
        System.out.println("    调用stackMethod2() -> 创建栈帧3");
        int result = 1 + 2; // 操作数栈演示：将1和2压入操作数栈，执行加法，结果压入栈
        System.out.println("    计算结果：" + result + " -> 使用操作数栈进行计算");
        System.out.println("    stackMethod2()执行完毕 -> 销毁栈帧3");
    }
    
    /**
     * 程序计数器演示
     */
    private static void demonstrateProgramCounter() {
        System.out.println("\n=== 程序计数器演示 ===");
        
        System.out.println("\n程序计数器作用：");
        System.out.println("1. 记录当前线程执行的字节码指令地址");
        System.out.println("2. 线程切换后能恢复到正确的执行位置");
        System.out.println("3. 分支、循环、跳转、异常处理都依赖程序计数器");
        
        System.out.println("\n字节码执行演示：");
        int a = 10;  // 对应字节码：bipush 10, istore_1
        int b = 20;  // 对应字节码：bipush 20, istore_2  
        int c = a + b; // 对应字节码：iload_1, iload_2, iadd, istore_3
        
        System.out.println("执行 int a = 10:");
        System.out.println("  程序计数器指向 bipush 10 指令");
        System.out.println("  执行后程序计数器指向下一条指令");
        
        System.out.println("\n执行 int c = a + b:");
        System.out.println("  程序计数器依次指向：");
        System.out.println("  1. iload_1 (加载变量a)");
        System.out.println("  2. iload_2 (加载变量b)");
        System.out.println("  3. iadd (执行加法)");
        System.out.println("  4. istore_3 (存储结果到变量c)");
        
        System.out.println("\n分支跳转演示：");
        if (c > 25) {
            System.out.println("条件为真，程序计数器跳转到if块内的指令");
        } else {
            System.out.println("条件为假，程序计数器跳转到else块内的指令");
        }
        
        System.out.println("\n循环控制演示：");
        for (int i = 0; i < 3; i++) {
            System.out.println("循环第" + (i+1) + "次，程序计数器控制循环跳转");
        }
        
        System.out.println("\n程序计数器特点：");
        System.out.println("1. 线程私有，每个线程都有独立的程序计数器");
        System.out.println("2. 内存占用很小，可以忽略不计");
        System.out.println("3. 唯一不会发生OutOfMemoryError的内存区域");
        System.out.println("4. 执行Native方法时，程序计数器值为空（Undefined）");
    }
    
    /**
     * 本地方法栈演示
     */
    private static void demonstrateNativeMethodStack() {
        System.out.println("\n=== 本地方法栈演示 ===");
        
        System.out.println("\n本地方法栈作用：");
        System.out.println("为虚拟机使用到的Native方法服务");
        System.out.println("Native方法是用其他语言（如C、C++）编写的方法");
        
        System.out.println("\nNative方法示例：");
        
        // 调用Native方法
        long currentTime = System.currentTimeMillis(); // Native方法
        System.out.println("System.currentTimeMillis(): " + currentTime);
        System.out.println("这是一个Native方法，由C语言实现");
        
        int hashCode = Object.class.hashCode(); // Native方法
        System.out.println("Object.hashCode(): " + hashCode);
        System.out.println("hashCode()也是Native方法");
        
        // 数组复制Native方法
        int[] source = {1, 2, 3, 4, 5};
        int[] dest = new int[5];
        System.arraycopy(source, 0, dest, 0, 5); // Native方法
        System.out.println("System.arraycopy()执行完毕，这也是Native方法");
        
        System.out.println("\n常见的Native方法：");
        System.out.println("1. Object.hashCode()");
        System.out.println("2. Object.clone()");
        System.out.println("3. System.currentTimeMillis()");
        System.out.println("4. System.arraycopy()");
        System.out.println("5. Thread.start()");
        System.out.println("6. 文件I/O相关方法");
        System.out.println("7. 网络I/O相关方法");
        
        System.out.println("\n本地方法栈特点：");
        System.out.println("1. 线程私有");
        System.out.println("2. 在HotSpot虚拟机中，本地方法栈和虚拟机栈合并");
        System.out.println("3. 可能抛出StackOverflowError和OutOfMemoryError");
        System.out.println("4. 栈的大小可以固定也可以动态扩展");
        
        System.out.println("\nJNI（Java Native Interface）：");
        System.out.println("Java调用Native方法的标准接口");
        System.out.println("允许Java代码调用其他语言编写的代码");
        System.out.println("提供了Java与底层系统交互的能力");
    }
    
    /**
     * 直接内存演示
     */
    private static void demonstrateDirectMemory() {
        System.out.println("\n=== 直接内存演示 ===");
        
        System.out.println("\n直接内存特点：");
        System.out.println("1. 不是JVM运行时数据区的一部分");
        System.out.println("2. 不在Java堆中分配");
        System.out.println("3. 直接向系统申请内存空间");
        System.out.println("4. 主要用于NIO操作");
        
        System.out.println("\n直接内存的使用场景：");
        System.out.println("1. NIO的DirectByteBuffer");
        System.out.println("2. Netty框架的零拷贝");
        System.out.println("3. 大数据处理框架");
        System.out.println("4. 缓存系统（如Redis）");
        
        // 演示DirectByteBuffer（简化演示，实际使用需要import java.nio.*）
        System.out.println("\nDirectByteBuffer示例：");
        System.out.println("// 分配直接内存");
        System.out.println("ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024);");
        System.out.println("// 这1024字节分配在直接内存中，不在Java堆中");
        
        System.out.println("\n直接内存 vs 堆内存：");
        System.out.println("直接内存优势：");
        System.out.println("1. 减少数据拷贝：避免Java堆到Native堆的拷贝");
        System.out.println("2. 提高I/O性能：特别是网络I/O和文件I/O");
        System.out.println("3. 不受GC影响：不会因为GC导致性能波动");
        
        System.out.println("\n直接内存劣势：");
        System.out.println("1. 分配和释放成本高");
        System.out.println("2. 不受JVM内存管理");
        System.out.println("3. 容易造成内存泄漏");
        System.out.println("4. 调试困难");
        
        System.out.println("\n直接内存回收：");
        System.out.println("1. 不由GC直接管理");
        System.out.println("2. 通过Cleaner机制回收");
        System.out.println("3. Full GC时会触发直接内存回收");
        System.out.println("4. 可以通过System.gc()建议回收");
        
        System.out.println("\n直接内存配置：");
        System.out.println("-XX:MaxDirectMemorySize=size  设置最大直接内存");
        System.out.println("默认值等于-Xmx设置的最大堆内存");
    }
    
    /**
     * 显示当前内存使用情况
     */
    private static void showMemoryUsage() {
        System.out.println("\n=== 当前内存使用情况 ===");
        
        // 获取内存管理Bean
        MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
        
        // 堆内存使用情况
        MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
        System.out.println("\n堆内存使用情况：");
        System.out.println("初始大小：" + formatBytes(heapUsage.getInit()));
        System.out.println("已使用：" + formatBytes(heapUsage.getUsed()));
        System.out.println("已提交：" + formatBytes(heapUsage.getCommitted()));
        System.out.println("最大大小：" + formatBytes(heapUsage.getMax()));
        
        // 非堆内存使用情况（方法区/元空间）
        MemoryUsage nonHeapUsage = memoryBean.getNonHeapMemoryUsage();
        System.out.println("\n非堆内存使用情况（方法区/元空间）：");
        System.out.println("初始大小：" + formatBytes(nonHeapUsage.getInit()));
        System.out.println("已使用：" + formatBytes(nonHeapUsage.getUsed()));
        System.out.println("已提交：" + formatBytes(nonHeapUsage.getCommitted()));
        System.out.println("最大大小：" + formatBytes(nonHeapUsage.getMax()));
        
        // Runtime信息
        Runtime runtime = Runtime.getRuntime();
        System.out.println("\nRuntime内存信息：");
        System.out.println("总内存：" + formatBytes(runtime.totalMemory()));
        System.out.println("空闲内存：" + formatBytes(runtime.freeMemory()));
        System.out.println("已使用内存：" + formatBytes(runtime.totalMemory() - runtime.freeMemory()));
        System.out.println("最大内存：" + formatBytes(runtime.maxMemory()));
        
        System.out.println("\n内存参数说明：");
        System.out.println("init：JVM启动时申请的初始内存");
        System.out.println("used：当前已使用的内存");
        System.out.println("committed：JVM已经向操作系统申请的内存");
        System.out.println("max：JVM可以使用的最大内存");
    }
    
    /**
     * 格式化字节数为可读格式
     */
    private static String formatBytes(long bytes) {
        if (bytes < 0) return "未知";
        
        String[] units = {"B", "KB", "MB", "GB", "TB"};
        int unitIndex = 0;
        double size = bytes;
        
        while (size >= 1024 && unitIndex < units.length - 1) {
            size /= 1024;
            unitIndex++;
        }
        
        return String.format("%.2f %s", size, units[unitIndex]);
    }
    
    /**
     * 内存溢出演示（注释掉避免程序崩溃）
     * 实际运行时需要设置JVM参数来观察不同类型的OOM
     */
    private static void demonstrateOutOfMemoryError() {
        System.out.println("\n=== 内存溢出演示 ===");
        
        System.out.println("\n常见的内存溢出类型：");
        
        System.out.println("\n1. 堆内存溢出：java.lang.OutOfMemoryError: Java heap space");
        System.out.println("原因：对象过多，堆内存不足");
        System.out.println("JVM参数：-Xms20m -Xmx20m");
        System.out.println("代码示例：");
        System.out.println("List<Object> list = new ArrayList<>();");
        System.out.println("while(true) { list.add(new Object()); }");
        
        System.out.println("\n2. 栈溢出：java.lang.StackOverflowError");
        System.out.println("原因：递归调用过深，栈帧过多");
        System.out.println("JVM参数：-Xss128k");
        System.out.println("代码示例：");
        System.out.println("public void recursion() { recursion(); }");
        
        System.out.println("\n3. 方法区溢出：java.lang.OutOfMemoryError: Metaspace");
        System.out.println("原因：动态生成类过多，元空间不足");
        System.out.println("JVM参数：-XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m");
        System.out.println("常见场景：CGLib动态代理、JSP编译、动态语言");
        
        System.out.println("\n4. 直接内存溢出：java.lang.OutOfMemoryError: Direct buffer memory");
        System.out.println("原因：直接内存分配过多");
        System.out.println("JVM参数：-XX:MaxDirectMemorySize=10m");
        System.out.println("常见场景：NIO、Netty应用");
        
        System.out.println("\n5. 无法创建本地线程：java.lang.OutOfMemoryError: unable to create new native thread");
        System.out.println("原因：线程过多，系统无法创建新线程");
        System.out.println("解决：减少线程数量，增加系统线程限制");
        
        System.out.println("\n预防内存溢出的方法：");
        System.out.println("1. 合理设置JVM内存参数");
        System.out.println("2. 及时释放不需要的对象引用");
        System.out.println("3. 避免内存泄漏（如监听器未移除）");
        System.out.println("4. 使用内存分析工具（如MAT、JProfiler）");
        System.out.println("5. 定期监控内存使用情况");
        System.out.println("6. 优化数据结构和算法");
    }
}